 /*******************************************************************************
  * Copyright (c) 2000, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.editors.text;

 import java.util.ArrayList ;
 import java.util.Iterator ;
 import java.util.List ;
 import java.util.ResourceBundle ;

 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.content.IContentDescription;

 import org.eclipse.jface.action.IAction;
 import org.eclipse.jface.action.IMenuListener;
 import org.eclipse.jface.action.IMenuManager;
 import org.eclipse.jface.action.MenuManager;
 import org.eclipse.jface.action.Separator;
 import org.eclipse.jface.dialogs.IInputValidator;
 import org.eclipse.jface.dialogs.InputDialog;
 import org.eclipse.jface.window.Window;

 import org.eclipse.ui.IActionBars;
 import org.eclipse.ui.IEditorInput;
 import org.eclipse.ui.IFileEditorInput;
 import org.eclipse.ui.IWorkbenchActionConstants;
 import org.eclipse.ui.actions.ActionGroup;
 import org.eclipse.ui.internal.editors.text.NLSUtility;
 import org.eclipse.ui.texteditor.ITextEditor;
 import org.eclipse.ui.texteditor.IUpdate;
 import org.eclipse.ui.texteditor.ResourceAction;
 import org.eclipse.ui.texteditor.RetargetTextEditorAction;
 import org.eclipse.ui.texteditor.TextEditorAction;


 /**
  * Action group for encoding actions.
  * @since 2.0
  * @deprecated As of 3.1, encoding needs to be changed via properties dialog
  */
 public class EncodingActionGroup extends ActionGroup {

     private static final String FILE_CONTENT_ENCODING_FORMAT= TextEditorMessages.ResourceInfo_fileContentEncodingFormat;
     private static final String FILE_CONTAINER_ENCODING_FORMAT= TextEditorMessages.ResourceInfo_fileContainerEncodingFormat;


     /**
      * Action for setting the encoding of the editor to the value this action has
      * been initialized with.
      */
     static class PredefinedEncodingAction extends TextEditorAction {

         /** The target encoding of this action. */
         private String fEncoding;
         /** The action label. */
         private String fLabel;
         /** Indicates whether the target encoding is the default encoding. */
         private boolean fIsDefault;

         /**
          * Creates a new action for the given specification.
          *
          * @param bundle the resource bundle
          * @param prefix the prefix for lookups from the resource bundle
          * @param encoding the target encoding
          * @param editor the target editor
          */
         public PredefinedEncodingAction(ResourceBundle bundle, String prefix, String encoding, ITextEditor editor) {
             super(bundle, prefix, editor);
             fEncoding= encoding;
             if (prefix == null)
                 setText(encoding);
             fLabel= getText();
         }

         /**
          * Creates a new action for the given specification.
          *
          * @param bundle the resource bundle
          * @param encoding the target encoding
          * @param editor the target editor
          */
         public PredefinedEncodingAction(ResourceBundle bundle, String encoding, ITextEditor editor) {
             super(bundle, null, editor);
             fEncoding= encoding;
             setText(encoding);
             fLabel= getText();
         }

         /**
          * Returns the encoding support of the action's editor.
          *
          * @return the encoding support of the action's editor or <code>null</code> if none
          */
         private IEncodingSupport getEncodingSupport() {
             ITextEditor editor= getTextEditor();
             if (editor != null)
                 return (IEncodingSupport) editor.getAdapter(IEncodingSupport.class);
             return null;
         }

         /*
          * @see IAction#run()
          */
         public void run() {
             IEncodingSupport s= getEncodingSupport();
             if (s != null)
                 s.setEncoding(fIsDefault ? null : fEncoding);
         }

         /**
          * Returns the encoding currently used in the given editor.
          *
          * @param editor the editor
          * @return the encoding currently used in the given editor or <code>null</code> if no encoding support is installed
          */
         private String getEncoding(ITextEditor editor) {

             IEditorInput input= (editor.getEditorInput());
             if (input instanceof IFileEditorInput) {
                 IFile file= ((IFileEditorInput)input).getFile();
                 try {
                     String explicitEncoding;
                     explicitEncoding= file.getCharset(false);
                     if (explicitEncoding == null)
                         return null;
                 } catch (CoreException e) {
                     // continue - assume file is not using default encoding
 }
             }

             IEncodingSupport s= getEncodingSupport();
             if (s != null)
                 return s.getEncoding();

             return null;
         }

         /*
          * @see IUpdate#update()
          */
         public void update() {

             if (fEncoding == null) {
                 setEnabled(false);
                 return;
             }

             ITextEditor editor= getTextEditor();
             if (editor == null) {
                 setEnabled(false);
                 return;
             }

             // update label
 fIsDefault= IEncodingActionsConstants.DEFAULT.equals(fEncoding);
             if (fIsDefault)
                 setText(getDefaultEncodingText(editor, fLabel));
             else
                 setText(fLabel);

             // update enable state
 if (editor.isDirty())
                 setEnabled(false);
             else
                 setEnabled(true);

             // update checked state
 String current= getEncoding(editor);
             if (fIsDefault)
                 setChecked(current == null);
             else
                 setChecked(fEncoding.equals(current));
         }

     }

     /*
      * @since 3.0
      */
     private static String getDefaultEncodingText(ITextEditor editor, String defaultText) {
         IEditorInput input= (editor.getEditorInput());
         if (!(input instanceof IFileEditorInput))
             return defaultText;

         IFile file= ((IFileEditorInput)input).getFile();

         String format= FILE_CONTENT_ENCODING_FORMAT;
         String encoding;
         try {
             encoding= getEncodingFromContent(file);
             if (encoding == null) {
                 format= FILE_CONTAINER_ENCODING_FORMAT;
                 encoding= file.getParent().getDefaultCharset();
             }
         } catch (CoreException ex) {
             return defaultText;
         }

         return NLSUtility.format(format, encoding);
     }

     /*
      * @since 3.0
      */
     private static String getEncodingFromContent(IFile file) throws CoreException {
         IContentDescription description = file.getContentDescription();
         if (description != null) {
             byte[] bom= (byte[])description.getProperty(IContentDescription.BYTE_ORDER_MARK);
             if (bom == null)
                 return (String )description.getProperty(IContentDescription.CHARSET);
             if (bom == IContentDescription.BOM_UTF_8)
                 return TextEditorMessages.WorkbenchPreference_encoding_BOM_UTF_8;
             if (bom == IContentDescription.BOM_UTF_16BE)
                 return TextEditorMessages.WorkbenchPreference_encoding_BOM_UTF_16BE;
             if (bom == IContentDescription.BOM_UTF_16LE)
                 return TextEditorMessages.WorkbenchPreference_encoding_BOM_UTF_16LE;
         }

         return null;
     }

     /**
      * Sets the encoding of an editor to the value that has interactively been defined.
      */
     static class CustomEncodingAction extends TextEditorAction {


         /*
          * @see org.eclipse.ui.texteditor.TextEditorAction#TextEditorAction(ResourceBundle, String, ITextEditor)
          */
         protected CustomEncodingAction(ResourceBundle bundle, String prefix, ITextEditor editor) {
             super(bundle, prefix, editor);
         }

         /*
          * @see IUpdate#update()
          */
         public void update() {
             ITextEditor editor= getTextEditor();
             setEnabled(editor != null && !editor.isDirty());
         }

         /*
          * @see IAction#run()
          */
         public void run() {

             ITextEditor editor= getTextEditor();
             if (editor == null)
                 return;

             IEncodingSupport encodingSupport= (IEncodingSupport) editor.getAdapter(IEncodingSupport.class);
             if (encodingSupport == null)
                 return;

             String title= TextEditorMessages.Editor_ConvertEncoding_Custom_dialog_title;
             String message= TextEditorMessages.Editor_ConvertEncoding_Custom_dialog_message;
             IInputValidator inputValidator = new IInputValidator() {
                 public String isValid(String newText) {
                     return (newText == null || newText.length() == 0) ? " " : null; //$NON-NLS-1$
 }
             };

             String initialValue= encodingSupport.getEncoding();
             if (initialValue == null)
                 initialValue= encodingSupport.getDefaultEncoding();
             if (initialValue == null)
                 initialValue= ""; //$NON-NLS-1$

             InputDialog d= new InputDialog(editor.getSite().getShell(), title, message, initialValue, inputValidator);
             if (d.open() == Window.OK)
                 encodingSupport.setEncoding(d.getValue());
         }
     }


     /** List of predefined encodings. */
     private static final String [][] ENCODINGS;

     /** The default encoding. */
     private static final String SYSTEM_ENCODING;

     /**
      * Initializer: computes the set of predefined encoding actions.
      */
     static {

         String [][] encodings= {
             { IEncodingActionsConstants.DEFAULT, IEncodingActionsHelpContextIds.DEFAULT, IEncodingActionsDefinitionIds.DEFAULT },
             { IEncodingActionsConstants.US_ASCII, IEncodingActionsHelpContextIds.US_ASCII, IEncodingActionsDefinitionIds.US_ASCII },
             { IEncodingActionsConstants.ISO_8859_1, IEncodingActionsHelpContextIds.ISO_8859_1, IEncodingActionsDefinitionIds.ISO_8859_1 },
             { IEncodingActionsConstants.UTF_8, IEncodingActionsHelpContextIds.UTF_8, IEncodingActionsDefinitionIds.UTF_8 },
             { IEncodingActionsConstants.UTF_16BE, IEncodingActionsHelpContextIds.UTF_16BE, IEncodingActionsDefinitionIds.UTF_16BE },
             { IEncodingActionsConstants.UTF_16LE, IEncodingActionsHelpContextIds.UTF_16LE, IEncodingActionsDefinitionIds.UTF_16LE },
             { IEncodingActionsConstants.UTF_16, IEncodingActionsHelpContextIds.UTF_16, IEncodingActionsDefinitionIds.UTF_16 }
         };

         String system= System.getProperty("file.encoding"); //$NON-NLS-1$
 if (system != null) {

             int i;
             for (i= 0; i < encodings.length; i++) {
                 if (encodings[i][0].equals(system))
                     break;
             }

             if (i != encodings.length) {
                 // bring system encoding in second position - first is default encoding
 String [] s= encodings[i];
                 encodings[i]= encodings[1];
                 encodings[1]= s;
                 // forget system encoding as it's already in the list
 system= null;
             }
         }

         SYSTEM_ENCODING= system;
         ENCODINGS= encodings;
     }



     /** List of encoding actions of this group. */
     private List fRetargetActions= new ArrayList ();

     /**
      * Creates a new encoding action group for an action bar contributor.
      */
     public EncodingActionGroup() {

         fRetargetActions.add(new RetargetTextEditorAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.ConvertEncoding." + ENCODINGS[0][0] + ".", ENCODINGS[0][0], IAction.AS_RADIO_BUTTON)); //$NON-NLS-1$ //$NON-NLS-2$

         if (SYSTEM_ENCODING != null)
             fRetargetActions.add(new RetargetTextEditorAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.ConvertEncoding.System.", IEncodingActionsConstants.SYSTEM, IAction.AS_RADIO_BUTTON)); //$NON-NLS-1$

         for (int i= 1; i < ENCODINGS.length; i++)
             fRetargetActions.add(new RetargetTextEditorAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.ConvertEncoding." + ENCODINGS[i][0] + ".", ENCODINGS[i][0], IAction.AS_RADIO_BUTTON)); //$NON-NLS-1$ //$NON-NLS-2$

         fRetargetActions.add(new RetargetTextEditorAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.ConvertEncoding.Custom.", IEncodingActionsConstants.CUSTOM, IAction.AS_PUSH_BUTTON)); //$NON-NLS-1$
 }

     /*
      * @see ActionGroup#fillActionBars(org.eclipse.ui.IActionBars)
      */
     public void fillActionBars(IActionBars actionBars) {
         IMenuManager menuManager= actionBars.getMenuManager();
         IMenuManager editMenu= menuManager.findMenuUsingPath(IWorkbenchActionConstants.M_EDIT);
         if (editMenu != null && fRetargetActions.size() > 0) {
             MenuManager subMenu= new MenuManager(TextEditorMessages.Editor_ConvertEncoding_submenu_label);
             subMenu.addMenuListener(new IMenuListener() {
                 public void menuAboutToShow(IMenuManager manager) {
                     update();
                 }
             });

             Iterator e= fRetargetActions.iterator();
             subMenu.add((IAction) e.next());
             subMenu.add(new Separator());
             while (e.hasNext())
                 subMenu.add((IAction) e.next());

             editMenu.add(subMenu);
         }
     }

     /**
      * Retargets this action group to the given editor.
      *
      * @param editor the text editor to which the group should be retargeted
      */
     public void retarget(ITextEditor editor) {
         fTextEditor= editor;
         Iterator e= fRetargetActions.iterator();
         while (e.hasNext()) {
             RetargetTextEditorAction a= (RetargetTextEditorAction) e.next();
             a.setAction(editor == null ? null : editor.getAction(a.getId()));
         }
     }


     //------------------------------------------------------------------------------------------


     /** Text editor this group is associated with. */
     private ITextEditor fTextEditor;

     /**
      * Creates a new encoding action group for the given editor.
      *
      * @param editor the text editor
      */
     public EncodingActionGroup(ITextEditor editor) {

         fTextEditor= editor;

         ResourceAction a;
         if (SYSTEM_ENCODING != null) {
             a= new PredefinedEncodingAction(TextEditorMessages.getBundleForConstructedKeys(), SYSTEM_ENCODING, editor);
             a.setHelpContextId(IEncodingActionsHelpContextIds.SYSTEM);
             a.setActionDefinitionId(IEncodingActionsDefinitionIds.SYSTEM);
             editor.setAction(IEncodingActionsConstants.SYSTEM, a);
         }

         for (int i= 0; i < ENCODINGS.length; i++) {
             a= new PredefinedEncodingAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.ConvertEncoding." + ENCODINGS[i][0] + ".", ENCODINGS[i][0], editor); //$NON-NLS-1$ //$NON-NLS-2$
 a.setHelpContextId( ENCODINGS[i][1]);
             a.setActionDefinitionId( ENCODINGS[i][2]);
             editor.setAction(ENCODINGS[i][0], a);
         }

         a= new CustomEncodingAction(TextEditorMessages.getBundleForConstructedKeys(), "Editor.ConvertEncoding." + IEncodingActionsConstants.CUSTOM + ".", editor); //$NON-NLS-1$ //$NON-NLS-2$
 a.setHelpContextId(IEncodingActionsHelpContextIds.CUSTOM);
         a.setActionDefinitionId(IEncodingActionsDefinitionIds.CUSTOM);
         editor.setAction(IEncodingActionsConstants.CUSTOM, a);
     }

     /**
      * Updates all actions of this action group.
      */
     public void update() {
         if (fTextEditor == null)
             return;

         IAction a= fTextEditor.getAction(IEncodingActionsConstants.SYSTEM);
         if (a instanceof IUpdate)
             ((IUpdate) a).update();

         for (int i= 0; i < ENCODINGS.length; i++) {
             a= fTextEditor.getAction(ENCODINGS[i][0]);
             if (a instanceof IUpdate)
                 ((IUpdate) a).update();
         }

         a= fTextEditor.getAction(IEncodingActionsConstants.CUSTOM);
         if (a instanceof IUpdate)
             ((IUpdate) a).update();
     }

     /*
      * @see ActionGroup#dispose()
      */
     public void dispose() {
         if (fTextEditor != null) {
             fTextEditor.setAction(IEncodingActionsConstants.SYSTEM, null);
             for (int i= 0; i < ENCODINGS.length; i++)
                 fTextEditor.setAction(ENCODINGS[i][0], null);
             fTextEditor.setAction(IEncodingActionsConstants.CUSTOM, null);

             fTextEditor= null;
         }
     }
 }

